﻿using System;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using SdmxMl.Helper;

namespace SdmxMl.Common
{
    /// <summary>
    /// Base class for SM Artefacts and some Artefact content
    /// </summary>
    public class BaseArtefact : IAnnotationList, ISearchIdName
    {
        #region Properties

        public virtual ArtefactRefTypedList ArtefactRefList
        {
            get { return new ArtefactRefTypedList(); }
        }

        public virtual string Id { get; set; }

        protected string version;
        public virtual string Version
        {
            get { return version; }
            set
            {
                version = value;
                if (string.IsNullOrEmpty(version))
                    version = "1.0";
                //if (version.IndexOf('.') < 0)
                //    version += ".0";
            }
        }

        public virtual MultiLang Name { get; set; }

        public MultiLang Description { get; set; }

        public AnnotationList AnnotationList { get; set; }

        #endregion //Properties

        #region Constructors

        public BaseArtefact()
        {
            Init();
        }

        private void Init()
        {
            Name = new MultiLang();
            Description = new MultiLang();
            AnnotationList = new AnnotationList();
            Id = "Undefined";
            Version = "1.0";
        }

        public BaseArtefact(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr)
        {
            Init();
            Id = Xmlhelper.GetAtt(nodeArtefact, "id");
            Version = Xmlhelper.GetAtt(nodeArtefact, "version");
            
            MultiLangNodeReader mr = new MultiLangNodeReader();
            mr.Read(Name, nodeArtefact, "Name", nsmgr, false);
            mr.Read(Description, nodeArtefact, "Description", nsmgr, false);

            string query = nsmgr.VersionSdmx == SdmxVersion.SDMX_21 ?
                "./common:Annotations/common:Annotation" : "./structure:Annotations/common:Annotation";
            XmlNodeList nodeList = nodeArtefact.SelectNodes(query, nsmgr);
            foreach (XmlNode na in nodeList)
            {
                Annotation a = new Annotation(na, nsmgr);
                AnnotationList.Add(a);
            }
        }

        #endregion //Constructors

        #region Tree

        public virtual TreeNode FillNode(TreeNode parentNode)
        {
            TreeNode t = new TreeNode(this.ToString());
            t.Tag = this;
            parentNode.Nodes.Add(t);
            return t;
        }

        #endregion //Tree

        #region String Representation

        public override string ToString()
        {
            return Id;
        }
        #endregion // String Representation

    }


    /// <summary>
    /// General Base Class for all SM Artefacts
    /// </summary>
    public class Artefact : BaseArtefact, ISdmxArtefactRef, IComparable, ICheckInOutState
    {
        #region Properties

        public string AgencyId { get; set; }

        public string ValidFrom { get; set; }

        public string ValidTo { get; set; }

        public bool IsFinal { get; set; }

        /// <summary>
        /// The urn attribute holds a valid SDMX Registry URN (see SDMX Registry Specification for details).
        /// </summary>
        public virtual string Urn { get; set; }

        /// <summary>
        /// The uri attribute holds a URI that contains a link to a resource with additional information
        /// about the object, such as a web page. This uri is not a SDMX message.
        /// </summary>
        public string Uri { get; set; }

        public bool IsExternalReference { get; set; }

        #region SDMX 2.1

        /// <summary>
        /// The serviceURL attribute indicates the URL of an SDMX SOAP web service 
        /// from which the details of the object can be retrieved. Note that this can be a registry 
        /// or and SDMX structural metadata repository, as they both implement that same web service 
        /// interface.
        /// </summary>
        public string ServiceURL { get; set; }

        /// <summary>
        /// The structureURL attribute indicates the URL of a SDMX-ML structure message (in the same version
        /// as the source document) in which the externally referenced object is contained. Note that this
        /// my be a URL of an SDMX RESTful web service which will return the referenced object.
        /// </summary>
        public string StructureUrl { get; set; }

        /// <summary>
        /// The isPartial, if true, indicates that only the relevant portion of the item scheme is being
        /// communicated. This is used in cases where a codelist is returned for a key family in the context
        /// of a constraint.
        /// </summary>
        public bool IsPartial { get; set; }

        #endregion


        public string FullIdent
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(AgencyId).Append('+');
                sb.Append(Id).Append('+');
                sb.Append(Version);
                return sb.ToString();
            }
        }

        #endregion Properties

        #region Constructors

        public Artefact()
        {
            Init();
        }

        private void Init()
        {
            _regCheckInOutState = CheckInOutState.None;
        }


        public Artefact(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr)
            : base(nodeArtefact, nsmgr)
        {
            Init();
            AgencyId = Xmlhelper.GetAtt(nodeArtefact, "agencyID");
            ValidFrom = Xmlhelper.GetAtt(nodeArtefact, "validFrom");
            ValidTo = Xmlhelper.GetAtt(nodeArtefact, "validTo");
            IsFinal = Xmlhelper.GetAtt(nodeArtefact, "isFinal").ToLower() == "true";
            IsExternalReference = Xmlhelper.GetAtt(nodeArtefact, "isExternalReference").ToLower() == "true";
            Urn = Xmlhelper.GetAtt(nodeArtefact, "urn");
            Uri = Xmlhelper.GetAtt(nodeArtefact, "uri");

            if (nsmgr.VersionSdmx == SdmxVersion.SDMX_21)
            {
                IsPartial = Xmlhelper.GetAtt(nodeArtefact, "isPartial").ToLower() == "true"; ;
                ServiceURL = Xmlhelper.GetAtt(nodeArtefact, "serviceURL");
                StructureUrl = Xmlhelper.GetAtt(nodeArtefact, "structureURL");
            }

        }

        #endregion //Constructors

        #region XML serialize

        public virtual void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);

            // id required
            if (string.IsNullOrEmpty(Id))
                Id = "undefined";
            xw.WriteAttributeString("id", Id);

            Xmlhelper.WriteAttributeIfExist(xw, "version", Version);
            Xmlhelper.WriteAttributeIfExist(xw, "uri", Uri);
            Xmlhelper.WriteAttributeIfExist(xw, "urn", Urn);
            if (IsExternalReference)
                xw.WriteAttributeString("isExternalReference", IsExternalReference.ToString().ToLower());

            // agencyId required
            if (string.IsNullOrEmpty(AgencyId))
                AgencyId = "undefined";
            xw.WriteAttributeString("agencyID", AgencyId);

            if (IsFinal)
                Xmlhelper.WriteAttributeIfExist(xw, "isFinal", IsFinal.ToString().ToLower());

            if (SdmxNamespaceManager.SdmxTarget == SdmxVersion.SDMX_21)
            {
                if (IsPartial)
                    Xmlhelper.WriteAttributeIfExist(xw, "isPartial", IsPartial.ToString().ToLower());
                Xmlhelper.WriteAttributeIfExist(xw, "serviceURL", ServiceURL);
                Xmlhelper.WriteAttributeIfExist(xw, "structureURL", StructureUrl);

                //TODO valid from to
                Xmlhelper.WriteAttributeIfExist(xw, "validFrom", Xmlhelper.XsDateTime(ValidFrom));
                Xmlhelper.WriteAttributeIfExist(xw, "validTo", Xmlhelper.XsDateTime(ValidTo));

                // Annotations at start
                AnnotationList.WriteXml(xw, "common", null);
            }
            else
            {
                Xmlhelper.WriteAttributeIfExist(xw, "validFrom", ValidFrom);
                Xmlhelper.WriteAttributeIfExist(xw, "validTo", ValidTo);
            }

            // Name required
           if (Name.IsEmpty)
               Name.TextList.Add(new TextType(LangSupport.CurLang, "Undefined"));
            Name.WriteXml(xw, prefix, "Name");
            Description.WriteXml(xw, prefix, "Description");

            if (IsExternalReference == false)
                WriteXmlInsider(xw);

            // Annotations 2.0
            if (SdmxNamespaceManager.SdmxTarget == SdmxVersion.SDMX_20)
                AnnotationList.WriteXml(xw, prefix, null);

            xw.WriteEndElement();


        }

        public virtual void WriteXmlInsider(XmlTextWriter xw)
        {
        }

        #endregion //XML serialize

        #region String Representation

        public override string ToString()
        {
            string s = base.ToString() +  " - " + Name.ToString();
            if (Version != "1.0")
                s += " - " + Version;
            return s;

        }
        #endregion // String Representation

        #region Check

        /// <summary>Check Artefact consistency </summary>
        /// <param name="diagnostic">will contains error diagnostic if any</param>
        /// <returns>True on error, false otherwise</returns>
        public virtual bool Check(out string diagnostic)
        {
            //Return empty diagnostic in base class
            diagnostic = string.Empty;

            return false;
        }

        #endregion //Check

        #region IComparable Members

        public int CompareTo(object obj)
        {
            Artefact ar = obj as Artefact;

            if (ar == null)
                return -1;

            int rep = string.Compare(this.Id, ar.Id, false);
            if (rep == 0)
                rep = string.Compare(this.AgencyId, ar.AgencyId, false);
            if (rep == 0)
                rep = string.Compare(this.Version, ar.Version, false);
            return rep;
        }

        #endregion

        #region ICheckInOutState Members
        
        // To hold information (in memory only) about TinyRegistry checkIn state
        private CheckInOutState _regCheckInOutState;
        public CheckInOutState RegCheckState
        {
            get { return _regCheckInOutState; }
            set { _regCheckInOutState = value; }
        }
        
        #endregion

        #region Tree

        public override TreeNode FillNode(TreeNode parentNode)
        {
            TreeNode t = base.FillNode(parentNode);
            if (_regCheckInOutState == CheckInOutState.CheckIn)
                t.ImageIndex = t.SelectedImageIndex = 19;
            else if (_regCheckInOutState == CheckInOutState.CheckOut)
                t.ImageIndex = t.SelectedImageIndex = 18;

            return t;
        }

        #endregion //Tree

        #region Artefact Type

        public virtual Manager.SdmxArtefactType ArType
        {
            get
            {
                // Must be redefined in child classes
                return Manager.SdmxArtefactType.None;
            }
        }

        #endregion

    }
}
